/*
 * MODULE  NAME  :
 * PROGRAM NAME  : sbalance.h
 * AUTHOR        : HOTMOCHA
 * CREATE  DATE  : 2015-04-08 22:52:57
 * PROGRAM DESC  :
 *
 * HISTORY       :
 *
 */
#ifndef _H_SBALANCE_H_
#define _H_SBALANCE_H_

#include <netinet/in.h>
#include "sbrbtree.h"
#include "sblist.h"
#include <stdio.h>
#include "sbutil.h"
#include "sbatomic.h"
#include "sbconstant.h"

extern struct sb_cycle_env *g_env;

struct sb_conf_simple_address
{
	char simple_ip[IP_STR_LEN + 1];
	int ip1;
	int ip2;
	int ip3;
	int ip4;
	int port;
};
/* client address use */
struct sb_conf_range_address
{
	char range_ip[126+1];
	int num;
	int ip1_start, ip1_end;
	int ip2_start, ip2_end;
	int ip3_start, ip3_end;
	int ip4_start, ip4_end;
	int port;		
};

struct sb_conf_server
{
	struct sb_conf_simple_address simple_addr;

	int weight;

	int try_connect;	/* try_connect = 1, when connect failed should wait fail_timeout again */
	int fails;
	int max_fails;
	int fail_timeout;	/* ms */
	struct timeval  find_unhealth_time;	/* find unhealth server's last time */	
};

struct sb_forward_rule 
{
	char rule_name[1 + CONF_RULE_NAME_MAX_LEN];

	char balance_mode[21];
	int http_proxy;

	int listen_num;	
	struct sb_conf_simple_address listen_addr[CONF_LISTEN_NUM_PERRULE];

	int client_num;
	struct sb_conf_range_address client_addr[CONF_CLIENT_NUM_PERRULE];

	int server_num;
	struct sb_conf_server server_addr[CONF_SERVER_NUM_PERRULE];
	int server_total_weight;
	int current_server_index;	/* rr algorithm, current index */

	int connect_timeout;    /* ms scale */
	int keepalive_timeout;  /* ms scale */
};

struct sb_cycle_env;
struct sb_event;
struct sb_connection;
struct sb_listen;

struct sb_event_action
{
	int (*init)(struct sb_cycle_env*); 
	int (*done)(struct sb_cycle_env*); 
	int (*add_event)(struct sb_event*, int event);
	int (*del_event)(struct sb_event*, int event);
	int (*add_listen_event)(struct sb_event*);
	int (*del_listen_event)(struct sb_event*);
	int (*process_event)(struct sb_cycle_env*, int timeout, void *arg); 
};
extern struct sb_event_action event_action;
#define init_event              event_action.init
#define done_event              event_action.done
#define add_event               event_action.add_event
#define del_event               event_action.del_event
#define add_listen_event        event_action.add_listen_event
#define del_listen_event        event_action.del_listen_event
#define process_event   event_action.process_event

struct sb_net_address
{
	int port;
	char ip[IP_STR_LEN + 1];
	struct sockaddr_in sockaddr;
};

typedef int (*sb_event_handler)(struct sb_cycle_env *env, struct sb_event *ev);
struct sb_event
{
	/* can point to sb_connection and sb_listen*/
	void *data;
	sb_event_handler handler;

	unsigned int event_type;	/* listen fd, normal socket fd, file fd ... */

	/* kinds of flag */
	unsigned accept_ready:1;
	unsigned write_ready:1;
	unsigned read_ready:1;
	unsigned active:1;	/* register in event pool, active will be 1, and or not */
	unsigned timeout:1;	/* if the event is timeout set 1 */
	unsigned in_timer_set:1;	/* if the event is set in timer, in_timer_set flag will be 1 */
	unsigned force_delay_read:1;	/* when both side have read event, the first will force the another delete event, after read and send completly
									   add the other's read event */
	unsigned shutdown:1;		/* aready close status, now wait for connection's other side's read or write */
	unsigned error:1;		

        unsigned long timeout_time;  /* ms time */
        struct rb_node node;

	struct sb_event *next;
};

struct sb_listen
{
	int listen_fd;
	int listen_type;	/* listen_type:	manager listen port or normal client listen port, no use */
	struct sb_net_address listen_address;

	int remain;
	struct sb_event listen_event;

	struct sb_forward_rule *forward_rule;

	struct sb_listen *next;	/* no use */
}__attribute__((aligned(sizeof(long))));

struct sb_session;
struct sb_connection
{
	struct sb_net_address net_address;
	int sockfd;
	struct sb_event read;
	struct sb_event write;

	unsigned int connect_type;
	struct sb_session *session;
}__attribute__((aligned(sizeof(long))));

int sb_add_read_event(struct sb_event *ev, int read_handler(struct sb_cycle_env*, struct sb_event*));
int sb_add_write_event(struct sb_event *ev, int write_handler(struct sb_cycle_env*, struct sb_event*));
int sb_del_read_event(struct sb_event *ev);
int sb_del_write_event(struct sb_event *ev);

struct sb_session
{
	unsigned long sessionid;

	struct sb_connection client;
	struct sb_connection server;

	char cs_buf[MAXIOBUFLEN];
	unsigned int cs_used;
	char cs_buf_tmp[MAXIOBUFLEN];
	unsigned int cs_tmp_used;

	char sc_buf[MAXIOBUFLEN_2];
	unsigned int sc_used;

	unsigned int sessions_type;	/* for sb_session cache */
	struct sb_session *next;
	struct sb_session *prev;

	/* http proxy */
	char host[SB_DNS_MAX_HOSTNAME_LEN];
        void (*sb_dns_request_done)(struct sb_cycle_env*, struct sb_session*);
	int status;
	struct list_head dns_list_node;	/* for waiting for dns parsing */
	unsigned int content_length;	
	unsigned int cs_used_remain;	
	unsigned int current_offset;
	unsigned int request_times;	/* in this session, total get request number */
	unsigned int crlf:1;
	unsigned int http_connect_method:1;
	unsigned int dns_hit:1;		/* get ip from cache */
	unsigned int host_is_ip:1;	/* in http request header, host field use ip*/

	unsigned had_failed:1;		/* connect server has two chance, the first failed set had_failed 1 */
	unsigned had_finish:1;		/* sb_session had been recycled */
	unsigned connected_stage:1;	/* after connect ok*/
	int why_finish;		/* see above struct sb_session */
	struct sb_conf_server *select_server;	
	struct sb_forward_rule *select_rule;
};

struct sb_cycle_env
{
	int workerno;
	int msgqueueid;

	struct sb_session *active_session_list; /* using sessions */
	struct sb_session *reuse_session_list; /* recycle sessions */
	struct sb_session *new_reuse_session_list; /* in the current round get recycle sessions */

	unsigned long active_session_num;
	unsigned long connecting_server_num;
	unsigned long reuse_sessoion_num;
	unsigned long total_success;
	unsigned long total_session; 

	struct sb_listen listen[SYS_MAX_LISTEN_NUM];
	unsigned int total_listenfd_num;	/* should less than SYS_MAX_LISTEN_NUM */

	/***  config ***/
	int workers_num;
	int http_proxy;
	int rule_num;
	struct sb_forward_rule* rules[CONF_RULE_MAX_NUMS];
	int accept_delay_timeout;		/* system resource over load, need wait */
	int accept_dalay_flag;
	struct sb_event accept_delay_timer;

	/* muti process use */
	pid_t pid;
	sb_atomic_t *accept_lock;
};

extern struct timeval  cache_time;
extern char cache_str_time[sizeof("2014-12-12 12:12:12")];

inline static unsigned long sb_current_msec_time()
{
	unsigned long current_time = cache_time.tv_sec * 1000;
	current_time +=  cache_time.tv_usec / 1000;
	return current_time;
}

/* @INPUT timeout: ms scale */
inline static unsigned long sb_get_msec_timeout_time(unsigned long timeout)
{
	return sb_current_msec_time() + timeout;
}

#define sb_event_is_active(ev) ((ev)->active == 1)

int sb_transfer_read_event(struct sb_cycle_env *env, struct sb_event *e);
int sb_transfer_write_event(struct sb_cycle_env *env, struct sb_event *e);

struct sb_session* sb_new_session(struct sb_cycle_env *env);
void sb_destory_session(struct sb_session *sess);
void sb_list_push_session(struct sb_session **list, struct sb_session *sess);
struct sb_session* sb_list_pop_session(struct sb_session **list);
void sb_list_delete_session(struct sb_session **list, struct sb_session *sess);
void sb_recycle_session(struct sb_cycle_env *env, struct sb_session *sess);
struct sb_session* sb_get_session(struct sb_cycle_env *env);
struct sb_conf_server* sb_load_balance_get_server(struct sb_cycle_env *env, struct sb_forward_rule *rule, char *ip_port);

#endif
